--[[
    函数：lua2_search_design
    功能：搜索模版
    参数：ARGV[1]:types, 分类ID字符串,以半角逗号分割，形如：'1,2,3'
    参数：ARGV[2]:labels，标签ID字符串,以半角逗号分割，形如：'1,2,3'
    参数：ARGV[3]:colors，颜色ID字符串,以半角逗号分割，形如：'1,2,3'
    参数：ARGV[4]:price_type，费用类型，-1不限, 1免费, 2vip
    参数：ARGV[5]:keywords，关键字字符串,以半角逗号分割，形如：'蓝色,商务,名片'
    参数：ARGV[6]:page_num，页码，从0开始
    参数：ARGV[7]:page_size，页容量
    返回：返回数组：
            0:code,0表示成功，其它表示没搜索到结果中途退出
            1:result，搜索结果
            2:count，总页数
--]]

-- ########################################################
-- 分类的前缀
local PREFIX_TYPE = "type_"
-- 标签的前缀
local PREFIX_LABEL = "label_"
-- 颜色的前缀
local PREFIX_COLOR = "label_color_"
-- VIP的key
local KEY_VIP = "vip"
-- 排序的key
local KEY_SORT = "sort"
-- keyword的前缀
local PREFIX_KEYWORD = "kwd_"
-- ID-模版缓存的前缀
local PREFIX_DESIGN = "d_"

-- PPT的ID的前缀
local PREFIX_PPT = "p"
-- key的连接符号
local PREFIX_S = "*"
-- 数组字符串的分割符号
local PREFIX_ARRAY_SPLIT = ","

local MAX_SORTED_SET_LEN = 100

-- ########################################################
-- 组成返回值数组，第0个是错误码，第1个是搜索结果，第2个是总页数
local retData = function(code,result,count)
    local ret = {}
    table.insert(ret, code)

    if(result==nil)
    then
        result='[]'
    end
    table.insert(ret, result)

    if(count==nil)
    then
        count=0
    end
    table.insert(ret, count)
    return ret
end


-- ########################################################
-- 动态参数调用redis.call
local dynamicParaCall = function(cmd,key,array)
    -- https://stackoverflow.com/questions/42291690/redis-lua-dynamic-number-of-keys-in-redis-call
    local args = {cmd, key}
    local len = #array
    for i= 1, len do
        local key = array[i]
        table.insert(args, key)
    end

    -- unpack the table and pass to redis.pcall
    redis.call(unpack(args))
end

-- ########################################################
-- 动态参数调用redis.call,向数组中增加0
local dynamicParaCallZAddRange = function(key,array,start_i,end_i)
    -- https://stackoverflow.com/questions/42291690/redis-lua-dynamic-number-of-keys-in-redis-call
    local args = {'zadd', key}
    local len = #array
    for i= start_i, end_i do
        local mem = array[i]
        table.insert(args, 0)
        table.insert(args, mem)
    end

    -- unpack the table and pass to redis.pcall
    redis.call(unpack(args))
end

-- ########################################################
-- 动态参数调用redis.call,向数组中增加0
local dynamicParaCallZAddEx = function(key,array)
    -- https://stackoverflow.com/questions/42291690/redis-lua-dynamic-number-of-keys-in-redis-call
    local args = {'zadd', key}
    local len = #array
    local count = math.floor((len+MAX_SORTED_SET_LEN-1)/MAX_SORTED_SET_LEN)
    for i= 1, count do
        local start_i =  (i-1)*MAX_SORTED_SET_LEN+1
        local end_i =  i*MAX_SORTED_SET_LEN
        if end_i > len then
            end_i = len;
        end
        dynamicParaCallZAddRange(key,array,start_i, end_i)
    end
end

-- ########################################################
-- A,B作为中间结果
local A='A'
local B='B'
local C='C'
-- 删除旧数据
redis.call('del',A)
redis.call('del',B)
redis.call('del',C)

-- ########################################################
-- 分类ID数组types为空？
local no_type_flg=0
local types = string.split(ARGV[1], PREFIX_ARRAY_SPLIT)
local t_len = 0
if(types==nil)
then
    no_type_flg=1
else
    t_len = #types
    if(t_len==0)
    then
        no_type_flg=1
    end
end

if(no_type_flg==1)
then
    -- 如果types为空，则求D1的并集A
    local key = PREFIX_TYPE..PREFIX_S
    local l_keys = redis.call('keys',key)
    local l_len = #l_keys
    if(l_len<=1)
    then
        -- 内部数据错误：types数据错误
        return retData(1,nil,nil)
    else
        dynamicParaCall('SUNIONSTORE', A, l_keys)
    end

else
    -- 如果types有值，则根据types筛选D1后，求并集A
    local array = {}
    for i= 1, t_len do
        local key1 = PREFIX_TYPE..types[i]
        table.insert(array, key1)
    end
    dynamicParaCall('SUNIONSTORE', A, array)
end

-- 保护代码:A没有数据则退出
local A_len = redis.call('SCARD',A)
if(A_len==0)
then
    -- 内部数据错误：types数据错误
    return retData(2,nil,nil)
end


-- ########################################################
-- 标签ID字符串为空？
local no_label_flg=0
local labels = string.split(ARGV[2], PREFIX_ARRAY_SPLIT)
local label_len = 0
if(labels==nil)
then
    no_label_flg=1
else
    label_len = #labels
    if(label_len==0)
    then
        no_label_flg=1
    end
end

if(no_label_flg==0)
then
    -- 如果labels有值，则根据labels筛选D2(颜色内部先求并集)
    local array = {}
    for i= 1, label_len do
        local key1 = PREFIX_LABEL..labels[i]
        table.insert(array, key1)
    end
    redis.call('del',B)
    dynamicParaCall('SUNIONSTORE', B, array)

    -- 再与A求交集
    redis.call('SINTERSTORE', A, A, B)
end

-- 保护代码:A没有数据则退出
local A_len = redis.call('SCARD',A)
if(A_len==0)
then
    -- 内部数据错误：types数据错误
    return retData(3,nil,nil)
end


-- ########################################################
-- 颜色ID字符串为空？
local no_label_flg=0
local labels = string.split(ARGV[3], PREFIX_ARRAY_SPLIT)
local label_len = 0
if(labels==nil)
then
    no_label_flg=1
else
    label_len = #labels
    if(label_len==0)
    then
        no_label_flg=1
    end
end

if(no_label_flg==0)
then
    -- 如果colors有值，则根据labels筛选D25(颜色内部先求并集)
    local array = {}
    for i= 1, label_len do
        local key1 = PREFIX_COLOR..labels[i]
        table.insert(array, key1)
    end
    redis.call('del',B)
    dynamicParaCall('SUNIONSTORE', B, array)

    -- 再与A求交集
    redis.call('SINTERSTORE', A, A, B)
end

-- 保护代码:A没有数据则退出
local A_len = redis.call('SCARD',A)
if(A_len==0)
then
    -- 内部数据错误：types数据错误
    return retData(3,nil,nil)
end

-- ########################################################
-- 费用类型price_type为空？
local price_type = tonumber(ARGV[4]) or 0
if(price_type==2)
then
    local array = {A}
    table.insert(array, KEY_VIP)
    dynamicParaCall('SINTERSTORE', A, array)
elseif(price_type==1)
then
    local array = {A}
    table.insert(array, KEY_VIP)
    dynamicParaCall('SDIFFSTORE', A, array)
end

-- 保护代码:A没有数据则退出
local A_len = redis.call('SCARD',A)
if(A_len==0)
then
    -- 内部数据错误：types数据错误
    return retData(4,nil,nil)
end

-- ########################################################
-- 关键字数组keywords为空？
local no_kwd_flg=0
local keywords = string.split(ARGV[5], PREFIX_ARRAY_SPLIT)
local kwd_len = 0
if(keywords==nil)
then
    no_kwd_flg=1
else
    kwd_len = #keywords
    if(kwd_len==0)
    then
        no_kwd_flg=1
    end
end

if(no_kwd_flg==0)
then
    -- 如果keywords有值，根据keywords筛选D4后，得求并集D
    local array = {A}
    for i= 1, kwd_len do
        local key1 = PREFIX_KEYWORD..keywords[i]
        table.insert(array, key1)
    end
    dynamicParaCall('SINTERSTORE', A, array)
end

-- 保护代码:A没有数据则退出
local A_len = redis.call('SCARD',A)
if(A_len==0)
then
    -- 内部数据错误：types数据错误
    return retData(5,nil,nil)
end


-- ########################################################
-- 计算总页数
local page_num = tonumber(ARGV[6]) or 0
local page_size = tonumber(ARGV[7]) or 0
local count = math.floor((A_len+page_size-1)/page_size)

-- ########################################################
-- 按sort进行排序
local start_num = page_num*page_size
local end_num = start_num+page_size-1

local ids = redis.call('SMEMBERS',A)
redis.call('del',B)
redis.call('del',C)
dynamicParaCallZAddEx(B, ids)
redis.call('ZINTERSTORE',C,2,B,KEY_SORT)
-- 按递减排序
local pageIds =redis.call('ZREVRANGE',C,start_num,end_num)


-- ########################################################
-- 返回搜索结果
-- 提取当前页数据
local pageIdsLen = #pageIds
local result='['
for i= 1, pageIdsLen do
    local one_key = PREFIX_DESIGN..pageIds[i]
    local one_result = redis.call('hget',one_key, 'search_result')
    if(i>1)
    then
        -- 拼接符号
        result = result..PREFIX_ARRAY_SPLIT
    end
    result = result..one_result
end
local result=result..']'



-- ########################################################
--[[
-- 测试代码：返回A的数据
local TEST = redis.call('SMEMBERS',A)
if(1)
then
    return TEST
end
--]]




-- ########################################################
return retData(0,result,count)